#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Algorithm特定Side-Channel AttackExample
演示如何UseAlgorithm特定AttackModule进行针对性Attack
"""

import os
import sys
from pathlib import Path

# Add父DirectoryToPath
sys.path.insert(0, str(Path(__file__).parent.parent))

from algorithm_specific_attacks import get_algorithm_attack, ALGORITHM_ATTACKS


def run_aes_specific_attack():
    """RunAES特定AttackExample"""
    
    test_data_dir = Path(__file__).parent.parent / "power_traces"
    aes_traces = test_data_dir / "aes_test_traces.npz"
    
    if not aes_traces.exists():
        print(f"[FAIL] AESTestDataNot存In: {aes_traces}")
        return False
    
    print("[Crypto] AESAlgorithm特定AttackExample")
    print("-" * 50)
    
    # 获取True实Key
    import numpy as np
    data = np.load(aes_traces, allow_pickle=True)
    true_key = data['keys'][0]
    true_key_byte = true_key[0] if isinstance(true_key, (bytes, np.ndarray)) else true_key
    print(f"目标Key字节 0: 0x{true_key_byte:02x}")
    
    try:
        # CreateAES特定Attack实例
        aes_attack_class = get_algorithm_attack('aes')
        aes_attack = aes_attack_class()
        
        # LoadData
        if not aes_attack.load_data(str(aes_traces)):
            print("[FAIL] AESDataLoadFailed")
            return False
        
        print("[OK] AESDataLoadSuccess")
        
        # 获取AlgorithmParameters
        params = aes_attack.get_algorithm_parameters()
        print(f"AlgorithmParameters:")
        for key, value in params.items():
            print(f"  - {key}: {value}")
        
        # 获取可用的目标操作
        operations = aes_attack.get_target_operations()
        print(f"可用目标操作: {operations}")
        
        # 获取泄漏模型
        models = aes_attack.create_leakage_models()
        print(f"泄漏模型: {list(models.keys())}")
        
        print()
        
        # 1. AES CPAAttack
        print("[Target] AES CPAAttack (S-boxOutput)")
        result = aes_attack.run_cpa_attack(target_operation="sbox_output", target_byte=0)
        
        print(f"AttackResult:")
        print(f"  - Algorithm: {result.algorithm}")
        print(f"  - AttackClass型: {result.attack_type}")
        print(f"  - 目标操作: {result.target_operation}")
        print(f"  - Success: {result.success}")
        print(f"  - 置信度: {result.confidence:.4f}")
        print(f"  - Execute时间: {result.execution_time:.2f}秒")
        print(f"  - True实Key: 0x{true_key_byte:02x}")
        
        if result.recovered_key_bytes:
            print(f"  - 恢复的Key字节: {[hex(b) for b in result.recovered_key_bytes]}")
            # CheckKeyYesNo正确
            if len(result.recovered_key_bytes) > 0:
                recovered_byte = result.recovered_key_bytes[0]
                if recovered_byte == true_key_byte:
                    print("[Success] Key恢复正确!")
                else:
                    print("[WARNING] Key恢复Error!")
        
        print()
        
        # 2. AES DPAAttack
        print("[Target] AES DPAAttack (S-boxOutput)")
        result = aes_attack.run_dpa_attack(target_operation="sbox_output", target_byte=0)
        
        print(f"AttackResult:")
        print(f"  - Success: {result.success}")
        print(f"  - 置信度: {result.confidence:.4f}")
        print(f"  - Execute时间: {result.execution_time:.2f}秒")
        print(f"  - True实Key: 0x{true_key_byte:02x}")
        
        if result.recovered_key_bytes:
            print(f"  - 恢复的Key字节: {[hex(b) for b in result.recovered_key_bytes]}")
            if len(result.recovered_key_bytes) > 0:
                recovered_byte = result.recovered_key_bytes[0]
                if recovered_byte == true_key_byte:
                    print("[Success] Key恢复正确!")
                else:
                    print("[WARNING] Key恢复Error!")
        
        print()
        
        # 3. TestNot同目标操作
        print("[Search] TestNot同目标操作:")
        test_operations = ["sbox_output", "sbox_input", "first_round"]
        
        for operation in test_operations:
            if operation in operations:
                try:
                    result = aes_attack.run_cpa_attack(target_operation=operation, target_byte=0)
                    print(f"  {operation}: 置信度 {result.confidence:.4f}, Success: {result.success}")
                except Exception as e:
                    print(f"  {operation}: AttackFailed - {e}")
            else:
                print(f"  {operation}: NotSupport")
        
        return True
        
    except Exception as e:
        print(f"[FAIL] AES特定AttackFailed: {e}")
        return False


def run_sm4_specific_attack():
    """RunSM4特定AttackExample"""
    
    test_data_dir = Path(__file__).parent.parent / "power_traces"
    sm4_traces = test_data_dir / "sm4_test_traces.npz"
    
    if not sm4_traces.exists():
        print(f"[FAIL] SM4TestDataNot存In: {sm4_traces}")
        return False
    
    print("[Crypto] SM4Algorithm特定AttackExample")
    print("-" * 50)
    
    # 获取True实Key
    import numpy as np
    data = np.load(sm4_traces, allow_pickle=True)
    true_key = data['keys'][0]
    true_key_byte = true_key[0] if isinstance(true_key, (bytes, np.ndarray)) else true_key
    print(f"目标Key字节 0: 0x{true_key_byte:02x}")
    
    try:
        # CreateSM4特定Attack实例
        sm4_attack_class = get_algorithm_attack('sm4')
        sm4_attack = sm4_attack_class()
        
        # LoadData
        if not sm4_attack.load_data(str(sm4_traces)):
            print("[FAIL] SM4DataLoadFailed")
            return False
        
        print("[OK] SM4DataLoadSuccess")
        
        # 获取AlgorithmParameters
        params = sm4_attack.get_algorithm_parameters()
        print(f"AlgorithmParameters:")
        for key, value in params.items():
            print(f"  - {key}: {value}")
        
        # 获取可用的目标操作
        operations = sm4_attack.get_target_operations()
        print(f"可用目标操作: {operations}")
        
        print()
        
        # SM4 CPAAttack
        print("[Target] SM4 CPAAttack")
        result = sm4_attack.run_cpa_attack(target_operation="sbox_output", target_byte=0)
        
        print(f"AttackResult:")
        print(f"  - Algorithm: {result.algorithm}")
        print(f"  - AttackClass型: {result.attack_type}")
        print(f"  - Success: {result.success}")
        print(f"  - 置信度: {result.confidence:.4f}")
        print(f"  - Execute时间: {result.execution_time:.2f}秒")
        print(f"  - True实Key: 0x{true_key_byte:02x}")
        
        if result.recovered_key_bytes:
            print(f"  - 恢复的Key字节: {[hex(b) for b in result.recovered_key_bytes]}")
            if len(result.recovered_key_bytes) > 0:
                recovered_byte = result.recovered_key_bytes[0]
                if recovered_byte == true_key_byte:
                    print("[Success] Key恢复正确!")
                else:
                    print("[WARNING] Key恢复Error!")
        
        return True
        
    except Exception as e:
        print(f"[FAIL] SM4特定AttackFailed: {e}")
        return False


def compare_algorithm_attacks():
    """CompareNot同Algorithm的Attack效果"""
    
    print("\n[Stats] AlgorithmAttack效果Compare")
    print("-" * 50)
    
    test_data_dir = Path(__file__).parent.parent / "power_traces"
    test_files = {
        'aes': test_data_dir / "aes_test_traces.npz",
        'sm4': test_data_dir / "sm4_test_traces.npz"
    }
    
    results = {}
    
    for algorithm, trace_file in test_files.items():
        if not trace_file.exists():
            print(f"[WARNING] {algorithm.upper()}TestDataNot存In，Skip")
            continue
        
        try:
            attack_class = get_algorithm_attack(algorithm)
            attack = attack_class()
            
            if attack.load_data(str(trace_file)):
                # RunCPAAttack
                result = attack.run_cpa_attack(target_operation="sbox_output", target_byte=0)
                results[algorithm] = {
                    'success': result.success,
                    'confidence': result.confidence,
                    'time': result.execution_time
                }
            else:
                results[algorithm] = {'error': 'DataLoadFailed'}
                
        except Exception as e:
            results[algorithm] = {'error': str(e)}
    
    # OutputCompareResult
    print(f"{'Algorithm':<8} {'Success':<6} {'置信度':<8} {'时间(秒)':<10}")
    print("-" * 40)
    
    for algorithm, result in results.items():
        if 'error' in result:
            print(f"{algorithm.upper():<8} {'Error':<6} {'-':<8} {'-':<10}")
        else:
            success_str = "[OK]" if result['success'] else "[FAIL]"
            print(f"{algorithm.upper():<8} {success_str:<6} {result['confidence']:<8.4f} {result['time']:<10.2f}")
    
    # 找出最佳AlgorithmAttack
    successful_attacks = [(alg, r) for alg, r in results.items() 
                         if 'error' not in r and r['success']]
    
    if successful_attacks:
        best_attack = max(successful_attacks, key=lambda x: x[1]['confidence'])
        print(f"\n[Best] 最易Attack的Algorithm: {best_attack[0].upper()} (置信度: {best_attack[1]['confidence']:.4f})")
    else:
        print("\n[WARNING] 没HasAlgorithmAttackSuccess")


def demonstrate_leakage_models():
    """演示泄漏模型的Use"""
    
    print("\n[Test] 泄漏模型演示")
    print("-" * 50)
    
    test_data_dir = Path(__file__).parent.parent / "power_traces"
    aes_traces = test_data_dir / "aes_test_traces.npz"
    
    if not aes_traces.exists():
        print("[FAIL] AESTestDataNot存In，Skip泄漏模型演示")
        return
    
    try:
        aes_attack_class = get_algorithm_attack('aes')
        aes_attack = aes_attack_class()
        aes_attack.load_data(str(aes_traces))
        
        # 获取泄漏模型
        models = aes_attack.create_leakage_models()
        
        print("可用泄漏模型:")
        for model_name, model_func in models.items():
            print(f"  - {model_name}")
            
            # 演示模型Use
            try:
                # UseExampleDataTest模型
                test_data = [0x00, 0x55, 0xAA, 0xFF]
                test_key = 0x2b
                
                print(f"    TestData: {[hex(d) for d in test_data]}")
                print(f"    TestKey: {hex(test_key)}")
                
                # Calculate泄漏值
                leakage_values = []
                for data in test_data:
                    leakage = model_func(data, test_key)
                    leakage_values.append(leakage)
                
                print(f"    泄漏值: {leakage_values}")
                print()
                
            except Exception as e:
                print(f"    模型TestFailed: {e}")
                print()
                
    except Exception as e:
        print(f"[FAIL] 泄漏模型演示Failed: {e}")


def show_available_algorithms():
    """DisplayAll可用的Algorithm"""
    
    print("\n[List] 可用AlgorithmList")
    print("-" * 50)
    
    print("Support的Algorithm:")
    for algorithm in ALGORITHM_ATTACKS.keys():
        try:
            attack_class = get_algorithm_attack(algorithm)
            instance = attack_class()
            params = instance.get_algorithm_parameters()
            operations = instance.get_target_operations()
            
            print(f"  [Crypto] {algorithm.upper()}")
            print(f"     - Key大小: {params.get('key_size', 'N/A')}")
            print(f"     - 块大小: {params.get('block_size', 'N/A')}")
            print(f"     - 目标操作数: {len(operations)}")
            print()
            
        except Exception as e:
            print(f"  [FAIL] {algorithm.upper()}: InitializeFailed - {e}")
            print()


if __name__ == "__main__":
    try:
        print("=" * 60)
        print("Algorithm特定Side-Channel AttackExample")
        print("=" * 60)
        
        # Display可用Algorithm
        show_available_algorithms()
        
        # RunAES特定Attack
        aes_success = run_aes_specific_attack()
        print()
        
        # RunSM4特定Attack
        sm4_success = run_sm4_specific_attack()
        print()
        
        # CompareAlgorithmAttack效果
        if aes_success or sm4_success:
            compare_algorithm_attacks()
        
        # 演示泄漏模型
        if aes_success:
            demonstrate_leakage_models()
        
        print("\n" + "=" * 60)
        print("Algorithm特定AttackExampleCompleted")
        print("=" * 60)
        
    except KeyboardInterrupt:
        print("\n[WARNING] 用户Interrupt操作")
    except Exception as e:
        print(f"\n[FAIL] ExampleRunFailed: {e}")
        sys.exit(1)
